﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;

namespace ANTLR01
{
    /// <summary>
    /// A Scope object enumerates the symbols availabe to the executing expression.
    /// </summary>
    /// <remarks>
    /// The GetSymbol method is used by the ExpressionEx.SymbolExpression method to
    /// access a symbol.  The indexer is a convenience tool that the programmer can
    /// use to get and set symbol data.
    /// </remarks>
    public class SimpleObject : DynamicObject, IEnumerable<Symbol>
    {
        #region Variables

        private List<Symbol> _list;
        private Expression<Func<SimpleObject, object>> _expression;
        private Func<SimpleObject, object> _function;

        #endregion

        #region Constructors

        public SimpleObject()
        {
            _list = new List<Symbol>();
        }

        #endregion

        #region Properties

        /// <remarks>
        /// If it isn't defined yet, it will be defined and set to null.
        /// </remarks>
        public dynamic this[string name]
        {
            get
            {
                return GetSymbol(name).Data;
            }
            set
            {
                EnsureThatSymbolExists(name);
                _list.Where(x => x.Name == name).First().Data = value;
            }
        }

        /// <summary>
        /// This is the code that will be executed when the Scope is dynamically invoked.
        /// </summary>
        public Expression<Func<SimpleObject, object>> Expression
        {
            get
            {
                return _expression;
            }
            set
            {
                _expression = value;
                _function = (_expression != null) ? _expression.Compile() : null;
            }
        }

        #endregion

        #region Methods

        #region Dynamic Access Methods

        public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
        {
            result = (_function != null) ? _function(this) : null;
            return true;
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = this[binder.Name];
            return true;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            this[binder.Name] = value;
            return true;
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            return _list.Select(x => x.Name);
        }

        #endregion

        /// <summary>
        /// This is used by ExpressionEx.SymbolExpression to retrieve a symbol.
        /// </summary>
        public Symbol GetSymbol(string name)
        {
            EnsureThatSymbolExists(name);
            return _list.Where(x => x.Name == name).First();
        }

        public IEnumerator<Symbol> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        private void EnsureThatSymbolExists(string symbolName)
        {
            if (!_list.Any(x => x.Name == symbolName))
            {
                _list.Add(new Symbol() { Name = symbolName, Data = null });
            }
        }

        #endregion
    }
}